<?php

namespace ltcms\lib;

use ReflectionClass;
use ReflectionMethod;

class Event
{
    /**
     * 监听者
     */
    protected $listener = [];

    /**
     * 事件别名
     * @var array
     */
    protected $bind = [];

    private static $_instance;
    private function __construct(){}
    private function __clone(){ }

    /**
     * 获取类对象
     */
    public static  function getInstance(){
        if(!(self::$_instance instanceof self)){
            self::$_instance=new self();
        }
        return self::$_instance;
    }

    /**
     * 初始化事件
     */
    public function loader()
    {
        //遍历配置目录
        $listen=config("event.listen",array());
        $subscribe=config("event.subscribe",array());
        $bind=config("event.bind",array());
        $this->bind($bind);
        $this->listenEvents($listen);
        $this->subscribe($subscribe);
    }

    /**
     * 批量注册事件监听
     */
    public function listenEvents(array $events)
    {
        foreach ($events as $event => $listeners) {
            if (isset($this->bind[$event])) {
                $event = $this->bind[$event];
            }
            $listener=isset($this->listener[$event])?$this->listener[$event]:array();
            $this->listener[$event] = array_merge($listener, $listeners);
        }
        return $this;
    }

    /**
     * 注册事件监听
     */
    public function listen($event, $listener)
    {
        if (isset($this->bind[$event])) {
            $event = $this->bind[$event];
        }
        $this->listener[$event][] = $listener;
        return $this;
    }

    /**
     * 是否存在事件监听
     */
    public function hasListener($event)
    {
        if (isset($this->bind[$event])) {
            $event = $this->bind[$event];
        }
        return isset($this->listener[$event]);
    }

    /**
     * 移除事件监听
     */
    public function remove($event)
    {
        if (isset($this->bind[$event])) {
            $event = $this->bind[$event];
        }
        unset($this->listener[$event]);
    }

    /**
     * 绑定事件别名
     */
    public function bind(array $events)
    {
        $this->bind = array_merge($this->bind, $events);
        return $this;
    }

    /**
     * 注册事件订阅者
     */
    public function subscribe($subscriber)
    {
        $subscribers = (array) $subscriber;
        foreach ($subscribers as $subscriber) {
            if (is_string($subscriber)) {
                $subscriber = app($subscriber);
            }
            if (method_exists($subscriber, 'subscribe')) {
                // 手动订阅
                $subscriber->subscribe($this);
            } else {
                // 智能订阅
                $this->observe($subscriber);
            }
        }
        return $this;
    }

    /**
     * 自动注册事件观察者
     */
    public function observe($observer, $prefix = '')
    {
        if (is_string($observer)) {
            $observer = app($observer);
        }
        $reflect = new ReflectionClass($observer);
        $methods = $reflect->getMethods(ReflectionMethod::IS_PUBLIC);
        if (empty($prefix) && $reflect->hasProperty('eventPrefix')) {
            $reflectProperty = $reflect->getProperty('eventPrefix');
            $reflectProperty->setAccessible(true);
            $prefix = $reflectProperty->getValue($observer);
        }
        foreach ($methods as $method) {
            $name = $method->getName();
            if (0 === strpos($name, 'on')) {
                $this->listen($prefix . substr($name, 2), [$observer, $name]);
            }
        }
        return $this;
    }

    /**
     * 触发事件
     */
    public function trigger($event, $params = null, $once = false)
    {
        if (is_object($event)) {
            $params = $event;
            $event  = get_class($event);
        }
        if (isset($this->bind[$event])) {
            $event = $this->bind[$event];
        }
        $result    = array();
        $listeners = isset($this->listener[$event]) ?$this->listener[$event]:array();
        if (strpos($event, '.')) {
            list($prefix, $event) = explode('.', $event, 2);
            if (isset($this->listener[$prefix . '.*'])) {
                $listeners = array_merge($listeners, $this->listener[$prefix . '.*']);
            }
        }
        $listeners = array_unique($listeners, SORT_REGULAR);
        foreach ($listeners as $key => $listener) {
            $result[$key] = $this->dispatch($listener, $params);
            if (false === $result[$key] || (!is_null($result[$key]) && $once)) {
                break;
            }
        }
        return $once ? end($result) : $result;
    }

    /**
     * 触发事件(只获取一个有效返回值)
     */
    public function until($event, $params = null)
    {
        return $this->trigger($event, $params, true);
    }

    /**
     * 执行事件调度
     */
    protected function dispatch($event, $params = null)
    {
        if (!is_string($event)) {
            $call = $event;
        }else {
            $obj  = app($event);
            $call = [$obj, 'handle'];
        }
        $params=is_null($params)?array():(is_array($params)?$params:array($params));
        $result = call_user_func_array($call, $params);
        return $result;
    }
}
